home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / delicious_bookmarks-2.0.64-fx.xpi / components / nsYBookmarkSyncService.js < prev    next >
Text File  |  2008-06-19  |  61KB  |  1,465 lines

  1. /**
  2.  * Documentation about bookmark sync
  3.  *   
  4.  * When the first instance of firefox browser is opened, the sync function is called. 
  5.  * First of all, the transaction list stored in the extra datasource is checked and all the 
  6.  * pending transactions are sent to the remote server. There are three types of transaction: 
  7.  * addBookmark, editBookmark and deleteBookmark.  Generally, the transaction list should be empty 
  8.  * when the sync function is called because all these transactions should be sent to the server 
  9.  * immediately after the user carries out an operation. e.g. adding/editing/deleteing a bookmark. 
  10.  * Then, a timeout is set to call sync functionnitself again after X mins (5 mins by default).  
  11.  * After that, a "update" request would be sent to server to get the last update time of user's 
  12.  * bookmarks and compare the remote time with the local time stored in the extra datasource. 
  13.  * If the local update time is empty/null, a full sync will be carried out. 
  14.  * If the remote update time is greater than the local time or local update time is equal 
  15.  * to -1, an incremental sync will be carried out.
  16.  * 
  17.  * Full sync - bookmarks are downloaded in chunks and are added to the local store.
  18.  * Incremental sync - the local bookmark hashes and the remote bookmark hashes are compared.  
  19.  * The bookmarks which only exist in the remote would be downloaded from the server and add to the local list.  
  20.  * The bookmarks which only exist in the local would be deleted from the local list. 
  21.  *
  22.  */
  23.  
  24. /*
  25.  * Constants
  26.  */
  27. const nsIYBookmarkSyncService = Components.interfaces.nsIYBookmarkSyncService;
  28. const nsISupports = Components.interfaces.nsISupports;
  29. const nsTimer = "@mozilla.org/timer;1";
  30. const nsITimer = Components.interfaces.nsITimer;
  31.  
  32. var CC = Components.classes;
  33. var CI = Components.interfaces;
  34.  
  35. var DEL_ADD_BOOKMARK_WAIT = 8 * 1000;
  36. var DEL_CHUNK_WAIT = 8 * 1000;
  37. var DEL_CHUNK_SIZE = 50;
  38. const DEL_SYNC_INTERVAL = 5;  //mins
  39. const DEL_IS_SYNCING_WAIT = 60 * 1000;
  40. const DEL_SENDING_TRANSACIIONS_WAIT = 5 * 1000;
  41. const DEL_HASH_REQUESET_AFTER_TRANS_WAIT = 5 * 1000;
  42.  
  43. const CLASS_ID = Components.ID("{723A9B07-CA88-4386-B916-5E180837EDA8}");
  44. const CLASS_NAME = "Sync Local Bookmarks with remote service";
  45. const CONTRACT_ID = "@mozilla.org/ybookmarks-sync-service;1";
  46.  
  47. const kDelContractID = "@yahoo.com/socialstore/delicious;1";
  48.  
  49. /**********************************************************
  50.  * Load yDebug.js
  51.  **********************************************************/
  52. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  53.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  54.         "chrome://ybookmarks/content/yDebug.js" ); 
  55.  
  56. /**********************************************************
  57.  * Load ybookmarksUitl.js
  58.  **********************************************************/
  59. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  60.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  61.         "chrome://ybookmarks/content/ybookmarksUtils.js" ); 
  62.  
  63. /**
  64.  *  Download the missing bookmarks from remote and add them to the local store
  65.  *
  66.  *  @param hashes the array of bookmark hashes for downloading the remote bookmarks
  67.  *  @param lastUpdateTime the last update time of the remote bookmarks
  68.  */
  69. function YBookmarkSyncService_download_diff (hashes, lastUpdateTime) {
  70.    
  71.    var delreader = Components.classes[kDelContractID].
  72.        getService(Components.interfaces.nsISocialStore);
  73.    
  74.    var cb = {
  75.       onload: function(posts) {
  76.       
  77.         var bookmarksStore =
  78.             (Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  79.              getService(Components.interfaces.nsIYBookmarksStoreService));
  80.         
  81.         var notifyData = null;
  82.         if ( posts.length ) {
  83.           notifyData = "add-to-ds-begin";
  84.           Components.classes["@mozilla.org/observer-service;1"].
  85.                getService(Components.interfaces.nsIObserverService).
  86.               notifyObservers(null, "ybookmark.syncInfo", notifyData); 
  87.         }
  88.         
  89.         var post;
  90.         for (var i = 0; i < posts.length; i++) {
  91.           post = 
  92.               posts.queryElementAt(i,
  93.                                    Components.interfaces
  94.                                    .nsIWritablePropertyBag);
  95.  
  96.           _YBookmarksSyncHelper.updateStoreFromPost( bookmarksStore, post );
  97.  
  98.           if ( i == (posts.length - 1)) {
  99.              notifyData = "add-to-ds-end";
  100.              Components.classes["@mozilla.org/observer-service;1"].
  101.                                 getService(Components.interfaces.nsIObserverService).
  102.                 notifyObservers(null, "ybookmark.syncInfo", notifyData); 
  103.           } 
  104.         }
  105.         
  106.          yDebug.print ( "We are done with incremental sync..." );
  107.          YBookmarksSyncService._isSyncing = false;
  108.          bookmarksStore.setLastUpdateTime(lastUpdateTime);
  109.          bookmarksStore.flush(false); 
  110.          notifyData = "all-done";
  111.  
  112.          Components.classes["@mozilla.org/observer-service;1"]
  113.            .getService(Components.interfaces.nsIObserverService)
  114.                    .notifyObservers(null, "ybookmark.syncDone", notifyData);        
  115.       
  116.       },
  117.       
  118.       onerror : function (posts) {
  119.          yDebug.print("UNABLE to download user's delicious bookmarks based on the hashes", YB_LOG_MESSAGE);
  120.          YBookmarksSyncService._isSyncing = false;
  121.          var notifyData = "add-to-ds-end";
  122.          Components.classes["@mozilla.org/observer-service;1"].
  123.               getService(Components.interfaces.nsIObserverService).
  124.          notifyObservers(null, "ybookmark.syncInfo", notifyData);
  125.       }
  126.    };
  127.    
  128.    if (hashes.length > 0) {
  129.      delreader.getBookmarksForHashes(hashes, cb);
  130.    }
  131.    else {
  132.      yDebug.print ( "We are done with incremental sync..." );   
  133.      var bookmarksStore =
  134.           (Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  135.             getService(Components.interfaces.nsIYBookmarksStoreService));
  136.      YBookmarksSyncService._isSyncing = false;
  137.      bookmarksStore.setLastUpdateTime(lastUpdateTime);
  138.      bookmarksStore.flush(false); 
  139.      var notifyData = "all-done";
  140.      Components.classes["@mozilla.org/observer-service;1"]
  141.      .getService(Components.interfaces.nsIObserverService)
  142.                    .notifyObservers(null, "ybookmark.syncDone", notifyData);        
  143.    }
  144. }
  145.  
  146. /**
  147.  * Delete some local bookmarks
  148.  * 
  149.  * @param hashes the array of bookmark hashes for deleting some local bookmarks
  150.  */
  151. function YBookmarkSyncService_delete_diff (hashes) {
  152.   
  153.   var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  154.            getService(Components.interfaces.nsIYBookmarksStoreService);         
  155.   var urlhash, url;
  156.   for (var i = 0; i < hashes.length; i++) {
  157.      urlhash = hashes.queryElementAt(i, Components.interfaces.nsISupportsString);
  158.      bookmarksStore.deleteBookmarkForHash(urlhash);
  159.   }
  160. }  
  161.  
  162. function _convertHashesFromNSArrayToJSObject(nsArray) {
  163.  
  164.   var jsArray = new Object();
  165.   var post, urlhash;
  166.   for (var i=0; i < nsArray.length; i++) {
  167.      post = nsArray.queryElementAt(i,
  168.                                    Components.interfaces
  169.                                    .nsIWritablePropertyBag);             
  170.      urlhash = post.getProperty("hash");
  171.      jsArray[urlhash] = post.getProperty("metahash");
  172.      //yDebug.print("Hash===>" + urlhash + ":" + jsArray[urlhash]);
  173.   }
  174.   
  175.   return jsArray;
  176. }
  177.  
  178. /**
  179.  * Compare the local bookmark hashes and remote bookmark hashes
  180.  *
  181.  * @param localHashList the array of local bookmark hashes
  182.  * @param remoteHashList the array of remote bookmark hashes
  183.  * @return reult the array of two sets of bookmarks hashes: one for downloading
  184.  * bookmarks from remote and one for deleting local bookmarks
  185.  *
  186.  */
  187. function _compareBookmarkHashes(localHashList, remoteHashList) {
  188.  
  189.   const NSArray = new Components.Constructor("@mozilla.org/array;1", 
  190.                                              Components.interfaces.nsIMutableArray);
  191.   const NSString = new Components.Constructor("@mozilla.org/supports-string;1",
  192.                                               Components.interfaces.nsISupportsString);
  193.  
  194.    var result = new Array();
  195.    var deleteList = new NSArray();
  196.    var downloadList = new NSArray();
  197.    var remoteMatch;
  198.  
  199.    var localMatch, str;
  200.    for (var remoteUrlHash in remoteHashList) {
  201.      
  202.      localMatch = localHashList[remoteUrlHash];
  203.      if (localMatch) {
  204.        if (remoteHashList[remoteUrlHash] != localMatch) {
  205.          yDebug.print("Bookmark was edited => download " + remoteUrlHash, YB_LOG_MESSAGE);
  206.          str = new NSString();
  207.          str.data = remoteUrlHash;
  208.          downloadList.appendElement(str, false);
  209.        }
  210.        localHashList[remoteUrlHash] = null;
  211.      }
  212.      else {
  213.        yDebug.print("New bookmark was added to the remote => download " + remoteUrlHash, YB_LOG_MESSAGE);
  214.        str = new NSString();
  215.        str.data = remoteUrlHash;       
  216.        downloadList.appendElement(str, false);
  217.      }
  218.    }  
  219.  
  220.    for (var localUrlHash in localHashList) {
  221.      if (localHashList[localUrlHash] != null) {
  222.        //remove from the list
  223.        yDebug.print("Delete local bookmark ---> " + localUrlHash);
  224.        str = new NSString();
  225.        str.data = localUrlHash;      
  226.        deleteList.appendElement(str, false);
  227.      }
  228.    }
  229.  
  230.    result["deleteList"] = deleteList;
  231.    result["downloadList"] = downloadList;
  232.    
  233.    return result;
  234. }     
  235.  
  236. /**
  237.  * Class definition
  238.  */
  239. var YBookmarksSyncService = {
  240.    _syncAllowed: true,
  241.    _isSyncing : false,
  242.    _backOffFromSync : false,
  243.    _serverErrorCount : 0,
  244.    _timer : null,  
  245.    
  246.       observe: function( subject, topic, data ) {          
  247.         if(topic == "ybookmark.internalServerStatus") {
  248.             yDebug.print("SyncService: observe, topic:" + topic + ",data:" + data);
  249.             // This is for testing purpose
  250.             /*
  251.             if(this._backOffFromSync == false) {
  252.               var pref = Components.classes["@mozilla.org/preferences-service;1"]
  253.                       .getService(Components.interfaces.nsIPrefBranch);                      
  254.               try {
  255.               data = pref.getIntPref("extensions.ybookmarks@yahoo.bookmark.test");
  256.               } catch (e) {}
  257.               yDebug.print("**************DATA ::data is now:" + data, YB_LOG_MESSAGE);
  258.             }*/
  259.                                   
  260.             if(data == "500"){                 
  261.                 if(this._backOffFromSync == false) {
  262.                     this._backOffFromSync = true;
  263.                     this._serverErrorCount++;
  264.                     if(this._serverErrorCount > 4) {
  265.                         this._serverErrorCount = 4;
  266.                     }                    
  267.                     if (! this._timer) {
  268.                       this._timer = CC[nsTimer].createInstance(nsITimer);
  269.                     } else {
  270.                       this._timer.cancel();                      
  271.                     }                    
  272.                     var timeout = Math.floor(Math.random(new Date().getSeconds()) * 60) + (this._serverErrorCount * 60);
  273.                     this._timer.initWithCallback(this, timeout * 60 *1000,
  274.                         CI.nsITimer.TYPE_ONE_SHOT);                    
  275.                     yDebug.print("SyncService: observe,Timeout is" + timeout + ",this._serverErrorCount:" + this._serverErrorCount, YB_LOG_MESSAGE);    
  276.                 }
  277.             } else if(data == "200"){                
  278.                 if(this._timer) { 
  279.                     this._timer.cancel();
  280.                     this._timer = null;
  281.                     this._backOffFromSync = false;
  282.                     YBookmarksSyncService.sync(true);
  283.                 }
  284.                 this._serverErrorCount = 0;
  285.                 this._backOffFromSync = false;                
  286.             }
  287.             //yDebug.print("SyncService: observe, _backOffFromSync:" + this._backOffFromSync + " _serverErrorCount" + this._serverErrorCount);
  288.         } else if(topic == "quit-application-granted") {
  289.             //TODO: Think about a misc component to handle dirty tricks like these. 
  290.             //:::Classic
  291.             try {
  292.                 var pref = Components.classes["@mozilla.org/preferences-service;1"].
  293.                          getService(Components.interfaces.nsIPrefBranch);
  294.                 var newMode = "";
  295.                 try {         
  296.                     newMode = pref.getCharPref("extensions.ybookmarks@yahoo.engine.set.mode");
  297.                 } catch(e) {}                
  298.                 if(newMode == YB_EXTENSION_MODE_CLASSIC || newMode == YB_EXTENSION_MODE_STANDARD) {
  299.                     pref.setCharPref("extensions.ybookmarks@yahoo.engine.current.mode", newMode);                    
  300.                     pref.setBoolPref("extensions.ybookmarks@yahoo.original.ui.hide", false);
  301.                     yDebug.print("SyncService:=>Changed to:" + newMode + " mode", YB_LOG_MESSAGE);    
  302.                 }                                
  303.                 //Reset the Bookmarks menu and Keybindings.
  304.                 if(newMode == YB_EXTENSION_MODE_CLASSIC) {
  305.                     pref.setBoolPref("extensions.ybookmarks@yahoo.original.keybindings.remap", false);
  306.                 } else if(newMode == YB_EXTENSION_MODE_STANDARD) {
  307.                     pref.setBoolPref("extensions.ybookmarks@yahoo.engine.revert.standard.mode", true);
  308.                     pref.setBoolPref("extensions.ybookmarks@yahoo.original.keybindings.remap", true);
  309.                 }
  310.                 pref.setCharPref("extensions.ybookmarks@yahoo.engine.set.mode", "");
  311.             } catch(e) {
  312.                 yDebug.print("SyncService:=>observe>quit-application-granted :" + e, YB_LOG_MESSAGE);    
  313.             }
  314.         }        
  315.     },    
  316.     
  317.     notify : function(aTimer) {
  318.         yDebug.print("SyncService: NOTIFY", YB_LOG_MESSAGE);  
  319.         //clear sync blocking flag.        
  320.         this._backOffFromSync = false;        
  321.         YBookmarksSyncService.sync(true);
  322.         this._timer = null;
  323.     },
  324.    
  325.    
  326.    init: function() {
  327.       yDebug.print( "Creating instance of YBookmarksSyncService" );
  328.  
  329.       var assClass =
  330.          Components.classes["@mozilla.org/appshell/appShellService;1"];
  331.       var ass = assClass.getService(Components.interfaces.nsIAppShellService);
  332.       gHiddenWin = ass.hiddenDOMWindow;
  333.       var observService = Components.classes[ "@mozilla.org/observer-service;1" ].
  334.       getService( Components.interfaces.nsIObserverService );
  335.       observService.addObserver( this, "ybookmark.internalServerStatus", false );
  336.       observService.addObserver( this, "quit-application-granted", false );   
  337.    },
  338.    
  339.    cancelSync: function() {
  340.       this._syncAllowed = false;
  341.       gHiddenWin.clearTimeout(gHiddenWin.ybSyncTimeoutId);
  342.       this._isSyncing = false;
  343.    },
  344.  
  345.    allowSync: function() {
  346.    
  347.       this._syncAllowed = true;
  348.       this._isSyncing = false;
  349.    },
  350.  
  351.    /**
  352.     * Send all the local transactions (e.g add, edit and delete bookmarks) to the remote.
  353.     * This is called periodically by the sync service but this should also be called 
  354.     * when a new transaction is added to the transaction store.
  355.     */  
  356.    processTransactions : function() {
  357.      try {
  358.       if (!this._syncAllowed) {
  359.          return;
  360.       }
  361.      
  362.      /* e.g. 0 - uninitialized, 1 - sent, 2 - completed, 3 - failed */
  363.      var socialStore = Components.classes["@yahoo.com/socialstore/delicious;1"].
  364.                          getService( Components.interfaces.nsISocialStore );
  365.  
  366.      var delreader = Components.classes[kDelContractID].
  367.           getService(Components.interfaces.nsISocialStore);
  368.  
  369.      var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  370.              getService(Components.interfaces.nsIYBookmarksStoreService);
  371.  
  372.      var transactionQueue = bookmarksStore.getTransactions();
  373.      
  374.      var transactions = transactionQueue.enumerate();
  375.      var transaction, transactionState, cb;
  376.            
  377.      while (transactions.hasMoreElements() && this._syncAllowed) {
  378.         
  379.         transaction = transactions.getNext();
  380.         transaction = transaction.QueryInterface(Components.interfaces.nsIWritablePropertyBag);        
  381.         
  382.         transactionState = transaction.getProperty("transactionState");
  383.         if (transactionState == 2 || transactionState == 1) {
  384.           continue;
  385.         }
  386.  
  387.         bookmarksStore.setTransactionState (transaction.getProperty("transactionType"), transaction.getProperty("url"), 1);
  388.         
  389.         switch (transaction.getProperty("transactionType")) {
  390.           case "addBookmark":
  391.             
  392.             cb = {
  393.               onload: function (returnValue) {
  394.  
  395.                 if(returnValue.length == 0){
  396.                   return;
  397.                 }  
  398.                 
  399.                 var rv = returnValue.queryElementAt(0, 
  400.                          Components.interfaces.nsIWritablePropertyBag);
  401.                 var url = rv.getProperty("url");
  402.  
  403.                 var bookmarksStore =Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  404.                          getService(Components.interfaces.nsIYBookmarksStoreService);
  405.                 bookmarksStore.setTransactionState ("addBookmark", url, 2);
  406.  
  407.                 yDebug.print("Added bookmark to the remote:" + url, YB_LOG_MESSAGE);
  408.                 gHiddenWin.setTimeout(function() { _YBookmarksSyncHelper.updateBookmarkHash(url); }, 
  409.                                          DEL_HASH_REQUESET_AFTER_TRANS_WAIT,
  410.                                          url);
  411.               },
  412.  
  413.               onerror: function (returnValue) {
  414.                 returnValue = returnValue.QueryInterface(Components.interfaces.nsIArray);
  415.                 if(returnValue.length == 0) {
  416.                   yDebug.print("Failed to add bookmark to the remote", YB_LOG_MESSAGE);
  417.                   return;
  418.                 }  
  419.                 
  420.                 var rv = returnValue.queryElementAt(0, 
  421.                         Components.interfaces.nsIWritablePropertyBag);
  422.                 var url = rv.getProperty("url");
  423.                 var status = rv.getProperty("status");
  424.                 var statusText = rv.getProperty("statusText");
  425.                 
  426.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  427.                       getService(Components.interfaces.nsIYBookmarksStoreService);
  428.                 if (status == "414") {
  429.                   yDebug.print("Remove addBookmark transaction: ", YB_LOG_MESSAGE);
  430.                   bookmarksStore.setTransactionState ("addBookmark", url, 2);
  431.  
  432.                   //remove bookmark from local store
  433.                   bookmarksStore.deleteBookmark(url);
  434.                   
  435.                   Components.classes["@mozilla.org/observer-service;1"]
  436.              .getService(Components.interfaces.nsIObserverService)
  437.                     .notifyObservers(null, "ybookmark.serverError", 
  438.                         '{ status:"' + status + '", action:"addBookmark" }');
  439.                 }
  440.                 else {
  441.                   bookmarksStore.setTransactionState ("addBookmark", url, 3);
  442.                 }
  443.  
  444.                 yDebug.print("Failed to add bookmark to the remote :" + url, YB_LOG_MESSAGE);
  445.               }
  446.             };       
  447.      
  448.             yDebug.print("Adding...");
  449.             delreader.addBookmark (transaction, cb);
  450.            
  451.           break;       
  452.           case "editBookmark":
  453.  
  454.             cb = {
  455.               onload: function (returnValue) {
  456.                 if(returnValue.length == 0)
  457.                   return;
  458.               
  459.                 var rv = returnValue.queryElementAt(0, 
  460.                         Components.interfaces.nsIWritablePropertyBag);
  461.                 var url = rv.getProperty("url");
  462.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  463.                         getService(Components.interfaces.nsIYBookmarksStoreService);
  464.                 bookmarksStore.setTransactionState ("editBookmark", url, 2);
  465.  
  466.                 yDebug.print("Edited Bookmark to remote :" + url, YB_LOG_MESSAGE);
  467.                 gHiddenWin.setTimeout(function(url) { _YBookmarksSyncHelper.updateBookmarkHash(url); }, 
  468.                                         DEL_HASH_REQUESET_AFTER_TRANS_WAIT,
  469.                                         url);
  470.               },
  471.  
  472.               onerror: function (returnValue) {
  473.                 returnValue = returnValue.QueryInterface(Components.interfaces.nsIArray);
  474.                 if (returnValue.length == 0) {
  475.                   yDebug.print("Failed to edit Bookmark to remote ", YB_LOG_MESSAGE);                                                           
  476.                   return;
  477.                 }
  478.                 
  479.                 var rv = returnValue.queryElementAt(0, 
  480.                         Components.interfaces.nsIWritablePropertyBag);
  481.                 var url = rv.getProperty("url");
  482.                 var status = rv.getProperty("status");
  483.                 var statusText = rv.getProperty("statusText");
  484.  
  485.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  486.                        getService(Components.interfaces.nsIYBookmarksStoreService);
  487.                 if (status == "414") {
  488.                   yDebug.print("Remove editBookmark transaction ", YB_LOG_MESSAGE);
  489.                   bookmarksStore.setTransactionState ("editBookmark", url, 2);               
  490.  
  491.                   //remove bookmark from local store
  492.                   bookmarksStore.deleteBookmark(url);
  493.  
  494.                   Components.classes["@mozilla.org/observer-service;1"]
  495.              .getService(Components.interfaces.nsIObserverService)
  496.                     .notifyObservers(null, "ybookmark.serverError", 
  497.                           '{ status:"' + status + '", action:"editBookmark" }');
  498.                 }
  499.                 else {
  500.                   bookmarksStore.setTransactionState ("editBookmark", url, 3);
  501.                 }
  502.  
  503.                 yDebug.print("Failed to edit Bookmark to remote :" + url, YB_LOG_MESSAGE);                                           
  504.               }
  505.             };       
  506.      
  507.             yDebug.print("Editing...");
  508.             delreader.editBookmark (transaction, cb);       
  509.         
  510.           break;
  511.           case "deleteBookmark":
  512.  
  513.             cb = {
  514.               onload: function (returnValue) {
  515.                 
  516.                 if (returnValue.length == 0)
  517.                   return;
  518.                 var rv = returnValue.queryElementAt(0, 
  519.                         Components.interfaces.nsIWritablePropertyBag);
  520.                 var url = rv.getProperty("url");
  521.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  522.                       getService(Components.interfaces.nsIYBookmarksStoreService);
  523.                  bookmarksStore.setTransactionState ("deleteBookmark", url, 2);
  524.  
  525.                 yDebug.print("Deleted Bookmark to remote :" + url, YB_LOG_MESSAGE);                                           
  526.               },
  527.  
  528.               onerror: function (returnValue) {
  529.                 returnValue = returnValue.QueryInterface(Components.interfaces.nsIArray);
  530.                 if (returnValue.length == 0) {
  531.                   yDebug.print("Failed to delete Bookmark to remote ", YB_LOG_MESSAGE);                                           
  532.                   return;
  533.                 }
  534.                 
  535.                 var rv = returnValue.queryElementAt(0, 
  536.                         Components.interfaces.nsIWritablePropertyBag);
  537.                 var url = rv.getProperty("url");        
  538.                 var status = rv.getProperty("status");
  539.                 var statusText = rv.getProperty("statusText");
  540.  
  541.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  542.                      getService(Components.interfaces.nsIYBookmarksStoreService);
  543.                 if (status == "414") {
  544.                   yDebug.print("Remove deleteBookmark transaction ", YB_LOG_MESSAGE);                  
  545.                   bookmarksStore.setTransactionState ("deleteBookmark", url, 2);
  546.                    Components.classes["@mozilla.org/observer-service;1"]
  547.              .getService(Components.interfaces.nsIObserverService)
  548.                     .notifyObservers(null, "ybookmark.serverError", 
  549.                        '{ status:"' + status + '", action:"deleteBookmark" }');
  550.                 }
  551.                 else {
  552.                   bookmarksStore.setTransactionState ("deleteBookmark", url, 3);
  553.                 }
  554.  
  555.                 yDebug.print("Failed to delete Bookmark to remote :" + url, YB_LOG_MESSAGE);                                           
  556.               }
  557.             };       
  558.      
  559.             yDebug.print("Deleting...");
  560.             delreader.deleteBookmark (transaction.getProperty("url"), cb);
  561.   
  562.           break;
  563.           case "setBundle":
  564.             
  565.             cb = {
  566.               onload: function (returnValue) {
  567.                 if(returnValue.length == 0){
  568.                   return;
  569.                 }  
  570.                                 
  571.                 var rv = returnValue.queryElementAt(0, 
  572.                          Components.interfaces.nsIWritablePropertyBag);
  573.                 
  574.   /*              var rvEnum = rv.enumerator;
  575.                 while (rvEnum.hasMoreElements()) {
  576.                   var e = rvEnum.getNext();
  577.                   e.QueryInterface(Components.interfaces.nsIProperty);
  578.                   yDebug.print("bundle name: " + e.name + "  value: " + e.value);
  579.                 }*/
  580.                 var bundle = rv.getProperty("url");
  581.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  582.                          getService(Components.interfaces.nsIYBookmarksStoreService);
  583.                 bookmarksStore.setTransactionState ("setBundle", bundle, 2);
  584.  
  585.                 yDebug.print("Added Bundle to the remote:" + bundle, YB_LOG_MESSAGE);
  586.                 /*gHiddenWin.setTimeout(function() { _YBookmarksSyncHelper.updateBookmarkHash(url); }, 
  587.                                          DEL_HASH_REQUESET_AFTER_TRANS_WAIT,
  588.                                          url);*/
  589.               },
  590.  
  591.               onerror: function (returnValue) {
  592.                 returnValue = returnValue.QueryInterface(Components.interfaces.nsIArray);
  593.                 if(returnValue.length == 0) {
  594.                   yDebug.print("Failed to add bundle to the remote", YB_LOG_MESSAGE);
  595.                   return;
  596.                 }  
  597.                 
  598.                 var rv = returnValue.queryElementAt(0, 
  599.                         Components.interfaces.nsIWritablePropertyBag);
  600.                 var url = rv.getProperty("url");
  601.                 var status = rv.getProperty("status");
  602.                 var statusText = rv.getProperty("statusText");
  603.                 
  604.                 var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  605.                       getService(Components.interfaces.nsIYBookmarksStoreService);
  606.                 if (status == "414") {
  607.                   yDebug.print("Remove setBundle transaction: ", YB_LOG_MESSAGE);
  608.                   bookmarksStore.setTransactionState ("setBundle", url, 2);
  609.  
  610.                   //remove bookmark from local store
  611.                   bookmarksStore.deleteBundle(url.substr(url.length));
  612.                   
  613.                   Components.classes["@mozilla.org/observer-service;1"]
  614.              .getService(Components.interfaces.nsIObserverService)
  615.                     .notifyObservers(null, "ybookmark.serverError", 
  616.                         '{ status:"' + status + '", action:"setBundle" }');
  617.                 }
  618.                 else {
  619.                   bookmarksStore.setTransactionState ("setBundle", url, 3);
  620.                 }
  621.  
  622.                 yDebug.print("Failed to set bundle to the remote :" + url, YB_LOG_MESSAGE);
  623.               }
  624.             };       
  625.      
  626.             yDebug.print("Setting Bundle...");
  627.         /*    var transEnum = transaction.enumerator;
  628.             while (transEnum.hasMoreElements()) {
  629.               var p = transEnum.getNext().QueryInterface(Components.interfaces.nsIProperty);
  630.               yDebug.print("bundle prop: " + p.name + " : " + p.value);
  631.             }*/
  632.             delreader.setBundle (transaction.getProperty("name"), transaction.getProperty("tags"), cb);
  633.            
  634.           break;       
  635.           case "deleteBundle":
  636.           
  637.             cb = {
  638.               onload: function (returnValue) {
  639.               if(returnValue.length == 0){
  640.                 return;
  641.               }  
  642.                               
  643.               var rv = returnValue.queryElementAt(0, 
  644.                        Components.interfaces.nsIWritablePropertyBag);
  645.               
  646. /*              var rvEnum = rv.enumerator;
  647.               while (rvEnum.hasMoreElements()) {
  648.                 var e = rvEnum.getNext();
  649.                 e.QueryInterface(Components.interfaces.nsIProperty);
  650.                 yDebug.print("bundle name: " + e.name + "  value: " + e.value);
  651.               }*/
  652.               var bundle = rv.getProperty("url");
  653.               var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  654.                        getService(Components.interfaces.nsIYBookmarksStoreService);
  655.               bookmarksStore.setTransactionState ("deleteBundle", bundle, 2);
  656.  
  657.               yDebug.print("Deleted Bundle from the remote:" + bundle, YB_LOG_MESSAGE);
  658.               /*gHiddenWin.setTimeout(function() { _YBookmarksSyncHelper.updateBookmarkHash(url); }, 
  659.                                        DEL_HASH_REQUESET_AFTER_TRANS_WAIT,
  660.                                        url);*/
  661.             },
  662.  
  663.             onerror: function (returnValue) {
  664.               returnValue = returnValue.QueryInterface(Components.interfaces.nsIArray);
  665.               if(returnValue.length == 0) {
  666.                 yDebug.print("Failed to delete bundle from the remote", YB_LOG_MESSAGE);
  667.                 return;
  668.               }  
  669.               
  670.               var rv = returnValue.queryElementAt(0, 
  671.                       Components.interfaces.nsIWritablePropertyBag);
  672.               var url = rv.getProperty("url");
  673.               var status = rv.getProperty("status");
  674.               var statusText = rv.getProperty("statusText");
  675.               
  676.               var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  677.                     getService(Components.interfaces.nsIYBookmarksStoreService);
  678.               
  679.               bookmarksStore.setTransactionState ("deleteBundle", url, 3);
  680.               
  681.               yDebug.print("Failed to set bundle to the remote :" + url, YB_LOG_MESSAGE);
  682.             }
  683.           };       
  684.    
  685.           yDebug.print("Deleting Bundle...");
  686.       /*    var transEnum = transaction.enumerator;
  687.           while (transEnum.hasMoreElements()) {
  688.             var p = transEnum.getNext().QueryInterface(Components.interfaces.nsIProperty);
  689.             yDebug.print("bundle prop: " + p.name + " : " + p.value);
  690.           }*/
  691.           delreader.deleteBundle (transaction.getProperty("name"), cb);
  692.           break;
  693.           default:
  694.         }
  695.      }
  696.    } catch (e) {
  697.      yDebug.print("ERROR PROCESSING TRANSACTIONS: " + e);
  698.    }
  699.    },
  700.       
  701.    /** 
  702.     * Sync the local store with the remote repository.
  703.     * This is done periodically by the service but this requests
  704.     * an immediate update.
  705.     *
  706.     * @param periodicSync a boolean to indicate whether we should run a 
  707.     * periodic sync or not.
  708.     */
  709.    sync: function(periodicSync) {
  710.          yDebug.print("nsYBookmarkSyncService.js::YBookmarksSyncService::sync()=> sync invoked...",YB_LOG_MESSAGE);
  711.       if (this != YBookmarksSyncService) { 
  712.         YBookmarksSyncService.sync(periodicSync);
  713.         return;
  714.       }
  715.  
  716.       if (!this._syncAllowed) {
  717.         return;
  718.       }
  719.       
  720.       if(periodicSync && this._backOffFromSync) {
  721.         yDebug.print("SyncService:->Sync() cancelled ->" + 
  722.             "_backOffFromSync:" + this._backOffFromSync, YB_LOG_MESSAGE); 
  723.         return;
  724.       }            
  725.       //yDebug.print("**************SyncService:->Sync() going on:", YB_LOG_MESSAGE);     
  726.             
  727.       var syncTimeoutId;
  728.       var bookmarksStore =
  729.          Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  730.             getService(Components.interfaces.nsIYBookmarksStoreService);             
  731.       
  732.       bookmarksStore.restateTransactions();
  733.       
  734.       //get all transactions with uninitialized state
  735.       if (bookmarksStore.getNumberOfTransactions("all", 0) > 0) {
  736.          
  737.          this.processTransactions();     
  738.      
  739.          //wait X seconds for the responses from the server
  740.          if (periodicSync) {
  741.             syncTimeoutId = gHiddenWin.setTimeout(function(syncService) { syncService.sync(true); }, DEL_SENDING_TRANSACIIONS_WAIT, this);
  742.             gHiddenWin.ybSyncTimeoutId = syncTimeoutId;
  743.          } else {
  744.             gHiddenWin.setTimeout(function(syncService) { syncService.sync(false); }, DEL_SENDING_TRANSACIIONS_WAIT, this);
  745.          }
  746.    
  747.          return;
  748.       }
  749.    
  750.       if (periodicSync) {
  751.       
  752.          //add something to here to get the preference
  753.         var interval = DEL_SYNC_INTERVAL;
  754.         var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  755.                       .getService(Components.interfaces.nsIPrefBranch);
  756.         try {
  757.           interval = prefs.getIntPref("extensions.ybookmarks@yahoo.bookmark.sync.interval");
  758.           if (interval <= 0) {
  759.             interval = DEL_SYNC_INTERVAL;
  760.             prefs.setIntPref("extensions.ybookmarks@yahoo.bookmark.sync.interval", interval);
  761.           }
  762.         }
  763.         catch(e) { }
  764.         interval *= (60 * 1000);
  765.                 
  766.         if (!this._isSyncing) {
  767.           syncTimeoutId =  gHiddenWin.setTimeout(function(syncService) { syncService.sync(true); }, interval, this);
  768.           gHiddenWin.ybSyncTimeoutId = syncTimeoutId;
  769.         } else {
  770.           yDebug.print("=====> Syncing at the moment, come back 1 min later");
  771.           syncTimeoutId =  gHiddenWin.setTimeout(function(syncService) { syncService.sync(true); }, DEL_IS_SYNCING_WAIT, this);
  772.           gHiddenWin.ybSyncTimeoutId = syncTimeoutId;
  773.           return;
  774.         }
  775.       } else {
  776.         if (this._isSyncing) {
  777.           yDebug.print("=====> Syncing at the moment, ignoring non-periodic sync");
  778.           return;  
  779.         }
  780.       }
  781.       
  782.       //If classic mode, dont sync.
  783.       if(ybookmarksUtils.getExtensionMode() == YB_EXTENSION_MODE_CLASSIC)  {
  784.           yDebug.print("nsYBookmarkSSyncService.js::YBookmarksSyncService::sync()=====> Classic mode, No sync.",YB_LOG_MESSAGE);
  785.           return;
  786.       }
  787.  
  788.       this._isSyncing = true; 
  789.  
  790.       //get from the server
  791.       var delreader =
  792.          Components.classes[kDelContractID].
  793.             getService(Components.interfaces.nsISocialStore);
  794.     
  795.       var cb = {
  796.          onload: function(posts) {
  797.             var post = 
  798.                posts.queryElementAt(0,
  799.                                     Components.interfaces
  800.                                     .nsIWritablePropertyBag);
  801.  
  802.             var dellastupdated = post.getProperty("time");
  803.             var newLinks4u = post.getProperty("inboxnew");
  804.  
  805.             yDebug.print("nsYBookmarkSSyncService.js::YBookmarksSyncService::sync()::cb::onload()=> inboxnew: "+newLinks4u,YB_LOG_MESSAGE);
  806.             
  807.             /**
  808.              * Notify event only if there are more than 0 new links
  809.              */
  810.             if(newLinks4u >= 0) {
  811.                 Components.classes["@mozilla.org/observer-service;1"].
  812.                          getService(Components.interfaces.nsIObserverService).
  813.                             notifyObservers(null, "ybookmark.updateLinks4u", newLinks4u);
  814.                 yDebug.print("nsYBookmarkSSyncService.js::YBookmarksSyncService::sync()::cb::onload()=> notified that there are new links",YB_LOG_MESSAGE);
  815.             }
  816.         
  817.             var bookmarksStore =
  818.                Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  819.                   getService(Components.interfaces.nsIYBookmarksStoreService);
  820.             var locallastupdate = bookmarksStore.getLastUpdateTime();
  821.  
  822.             yDebug.print("======> llu:" + locallastupdate + " dlu:" + dellastupdated);
  823.          
  824.             //locallastupdate == null, means we do not have a any data, force a full sync                 
  825.             //locallastupdate == "-1", means we do have data but we want to force a partial sync
  826.             if (locallastupdate) {
  827.                locallastupdate = parseInt(locallastupdate);
  828.                
  829.                if (isNaN(locallastupdate) || parseInt(dellastupdated) > locallastupdate) {
  830.                   //YBookmarksSyncService._syncFully(dellastupdated);
  831.                  YBookmarksSyncService._syncPartially(dellastupdated);
  832.                  yDebug.print("Do a partial sync here");
  833.                } else {
  834.                   Components.classes["@mozilla.org/observer-service;1"].
  835.                      getService(Components.interfaces.nsIObserverService).
  836.                         notifyObservers(null, "ybookmark.syncDone", "no-update");
  837.                   yDebug.print("No updates on delicious");
  838.  
  839.                   YBookmarksSyncService._isSyncing = false;
  840.                   
  841.                   return;
  842.                }
  843.             } else {
  844.               //full sync should only happen during the initial download .i.e. no data in the extra.rdf.
  845.               //full sync doesn't remove any bookmarks that aren't in the remote.
  846.               YBookmarksSyncService._syncFully(dellastupdated);          
  847.               yDebug.print("Do a full sync here");
  848.             }
  849.             
  850.             /* bundles */
  851.             // this is placed here because syncFully calls storeService.clearExtra() which calls clearBundles()
  852.             var bundleCb = {
  853.               onerror: function(event) {
  854.                 yDebug.print("nsYBookmarkSyncService.getBundles callback error!" + event);
  855.               },
  856.  
  857.               onload: function(bundles) {
  858.                 try {
  859.                   var bookmarksStore = Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  860.                           getService(Components.interfaces.nsIYBookmarksStoreService);
  861.  
  862.                   bookmarksStore.setBundles(bundles);
  863.  
  864.                 } catch (e) {
  865.                   yDebug.print("nsYBookmarkSyncService.getBundles callback.onload(): " + e);
  866.                 }
  867.               }
  868.             };
  869.             delreader.allBundles(bundleCb);
  870.                     
  871.          },
  872.  
  873.          onerror: function(event) {
  874.             yDebug.print("UNABLE to access user's update status", YB_LOG_MESSAGE);
  875.             yDebug.print("Notify the observer to update all windows\n");
  876.                   
  877.             YBookmarksSyncService._isSyncing = false;
  878.             Components.classes["@mozilla.org/observer-service;1"].
  879.               getService(Components.interfaces.nsIObserverService).
  880.                  notifyObservers(null, "ybookmark.syncDone", "sync-error");
  881.          }
  882.       };
  883.       
  884.       delreader.lastUpdate(cb);
  885.       
  886.    },
  887.    
  888.    _syncFully: function(lastUpdateTime) {
  889.       var bookmarksStore =
  890.          Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  891.             getService(Components.interfaces.nsIYBookmarksStoreService);
  892.             
  893.       //bugfix :there is a internal flag in this service to detemine whether we should delete all bookmarks
  894.       //the bookmarks can't be deleted when the bookmarks store service starts
  895.       
  896.       bookmarksStore.deleteAllBookmarks(true);
  897.       
  898.       Components.classes["@mozilla.org/observer-service;1"].
  899.          getService(Components.interfaces.nsIObserverService).
  900.             notifyObservers(null, "ybookmark.syncBegin", "no-update");
  901.       gHiddenWin.setTimeout(this._getChunk, 0, 0, lastUpdateTime);
  902.    },
  903.  
  904.    _syncPartially: function (dellastupdated) {
  905.  
  906.       if (this != YBookmarksSyncService) {
  907.         YBookmarksSyncService._syncPartially(dellastupdated);
  908.         return;
  909.       }
  910.  
  911.       var delreader =
  912.          Components.classes[kDelContractID].
  913.             getService(Components.interfaces.nsISocialStore);
  914.  
  915.       var cb = {
  916.          onload: function(remoteHashList) {
  917.             var bookmarksStore =
  918.                Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  919.                   getService(Components.interfaces.nsIYBookmarksStoreService);         
  920.             var localHashList = bookmarksStore.getBookmarkHashes();
  921.             var mLocalHashList = _convertHashesFromNSArrayToJSObject(localHashList);
  922.             var mRemoteHashList = _convertHashesFromNSArrayToJSObject(remoteHashList);
  923.             var lists = _compareBookmarkHashes(mLocalHashList, mRemoteHashList);
  924.             
  925.             //delete local bookmarks
  926.             var deleteList = lists["deleteList"];
  927.             if (deleteList.length > 0 ) {
  928.                YBookmarkSyncService_delete_diff (deleteList);
  929.             }        
  930.  
  931.             //download remote bookmarks
  932.             var downloadList = lists["downloadList"];        
  933.             //more than 100 bookmarks, we do a full sync instead
  934.             if (downloadList.length > 100) {
  935.                yDebug.print(" Going for full sync, downloadList.length is a big number: " + downloadList.length, YB_LOG_MESSAGE);
  936.                YBookmarksSyncService._syncFully(dellastupdated);
  937.             } else {
  938.                yDebug.print(" Going for a partial sync, downloadList.length is : " + downloadList.length, YB_LOG_MESSAGE);
  939.                YBookmarkSyncService_download_diff (downloadList, dellastupdated);
  940.             }
  941.          },
  942.       
  943.          onerror : function(posts) {
  944.            yDebug.print("UNABLE to access user's delicious hashes", YB_LOG_MESSAGE);
  945.            YBookmarksSyncService._isSyncing = false;
  946.          }
  947.       };
  948.  
  949.       delreader.getBookmarkHashes(cb);
  950.    },
  951.    
  952.    _getChunk: function(start, lastUpdateTime) {
  953.       if (this != YBookmarksSyncService) {
  954.         YBookmarksSyncService._getChunk(start, lastUpdateTime);
  955.         return;
  956.       }
  957.       
  958.       if (!this._syncAllowed) {
  959.          yDebug.print("Sync cancelled.");
  960.          yDebug.print("Notify the observer to update all windows\n");
  961.   
  962.          this._isSyncing = false;
  963.          Components.classes["@mozilla.org/observer-service;1"].
  964.            getService(Components.interfaces.nsIObserverService).
  965.               notifyObservers(null, "ybookmark.syncDone", "all-done");
  966.          return;
  967.       }
  968.  
  969.       var delreader =
  970.          Components.classes[kDelContractID].
  971.             getService(Components.interfaces.nsISocialStore);
  972.       var count = 0;
  973.       var downloadMore = false;
  974.    
  975.       var cb = {
  976.          startTime : (new Date()).getTime(),
  977.          onload: function(posts) {
  978.          
  979.             var notifyData = null;
  980.             if (!YBookmarksSyncService._syncAllowed) {
  981.                yDebug.print("Sync cancelled.");
  982.                yDebug.print("Notify the observer to update all windows\n");
  983.                
  984.                YBookmarksSyncService._isSyncing = false;
  985.                Components.classes["@mozilla.org/observer-service;1"].
  986.                  getService(Components.interfaces.nsIObserverService).
  987.               notifyObservers(null, "ybookmark.syncDone", "all-done");
  988.                return;
  989.             }
  990.  
  991.             var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  992.                       .getService(Components.interfaces.nsIPrefBranch);
  993.             var size = prefs.getIntPref("extensions.ybookmarks@yahoo.sync.chunk.size");
  994.             if (size >= 0 && DEL_CHUNK_SIZE != size) {
  995.               DEL_CHUNK_SIZE = size;
  996.             }
  997.             var wait = prefs.getIntPref("extensions.ybookmarks@yahoo.sync.chunk.wait");
  998.             if (wait >= 0 && DEL_CHUNK_WAIT != wait) {
  999.               DEL_CHUNK_WAIT = wait;
  1000.               DEL_ADD_BOOKMARK_WAIT = wait;
  1001.             }
  1002.                         
  1003.             var bookmarksStore =
  1004.                Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  1005.                   getService(Components.interfaces.nsIYBookmarksStoreService);
  1006.             
  1007.             if ( posts.length ) {
  1008.               notifyData = "add-to-ds-begin";
  1009.               Components.classes["@mozilla.org/observer-service;1"].
  1010.                getService(Components.interfaces.nsIObserverService).
  1011.               notifyObservers(null, "ybookmark.syncInfo", notifyData); 
  1012.             }
  1013.             
  1014.             var post;
  1015.             for (var i = 1; i < posts.length; i++) {
  1016.               post = 
  1017.                  posts.queryElementAt(i,
  1018.                                       Components.interfaces
  1019.                                       .nsIWritablePropertyBag);
  1020.  
  1021.               gHiddenWin.setTimeout(function(post, i) {
  1022.                  if (!YBookmarksSyncService._syncAllowed) {
  1023.                     yDebug.print("Sync cancelled.");
  1024.                     yDebug.print("Notify the observer to update all windows\n");
  1025.  
  1026.                     YBookmarksSyncService._isSyncing = false;
  1027.                     Components.classes["@mozilla.org/observer-service;1"].
  1028.                       getService(Components.interfaces.nsIObserverService).
  1029.                     notifyObservers(null, "ybookmark.syncDone", "all-done");
  1030.                     return;
  1031.                  }
  1032.  
  1033.                  _YBookmarksSyncHelper.updateStoreFromPost( bookmarksStore, post );
  1034.                                      
  1035.                  if ( i == (posts.length - 1)) {
  1036.                    notifyData = "add-to-ds-end";
  1037.                    Components.classes["@mozilla.org/observer-service;1"].
  1038.                     getService(Components.interfaces.nsIObserverService).
  1039.                      notifyObservers(null, "ybookmark.syncInfo", notifyData); 
  1040.                  } 
  1041.              }, 
  1042.              ((DEL_ADD_BOOKMARK_WAIT/DEL_CHUNK_SIZE) * i), 
  1043.              post, i);
  1044.            }
  1045.  
  1046.            var metaData = posts.queryElementAt(0,
  1047.                                       Components.interfaces
  1048.                                       .nsIWritablePropertyBag);
  1049.            var total = null;
  1050.            try {
  1051.              total = metaData.getProperty( "total" );
  1052.              yDebug.print ( "TOTAL => " + total, YB_LOG_MESSAGE);
  1053.            } catch ( e ) {
  1054.            }
  1055.            var syncDoneSubject = null;
  1056.            
  1057.            if ((posts.length - 1) == DEL_CHUNK_SIZE) {
  1058.              /* more chunks to go... */
  1059.              yDebug.print ( "More chunks to go.." );
  1060.              start += DEL_CHUNK_SIZE;
  1061.              var elapsedTime = (new Date()).getTime() - this.startTime;
  1062.              gHiddenWin.setTimeout(
  1063.                YBookmarksSyncService._getChunk, DEL_CHUNK_WAIT - elapsedTime,
  1064.                 start, lastUpdateTime);
  1065.              syncDoneSubject = { start: start, chunk: DEL_CHUNK_SIZE, total: total };
  1066.              syncDoneSubject.wrappedJSObject = syncDoneSubject;
  1067.              notifyData = "more-chunk";
  1068.            } else {
  1069.              /* we're done. */
  1070.              yDebug.print ( "We are done with sync..." );
  1071.              gHiddenWin.setTimeout(function() { 
  1072.                 if (lastUpdateTime) {
  1073.                     bookmarksStore.setLastUpdateTime(lastUpdateTime); 
  1074.                 } 
  1075.                 bookmarksStore.flush(false); 
  1076.                 yDebug.print("Synching done"); 
  1077.                 YBookmarksSyncService._isSyncing = false;
  1078.                }, 
  1079.                DEL_ADD_BOOKMARK_WAIT + 100);
  1080.              notifyData = "all-done";
  1081.           }
  1082.  
  1083.           gHiddenWin.setTimeout(function(notifyData){ 
  1084.             yDebug.print("Notify the observer to update all windows: " + notifyData);
  1085.             var os = Components.classes["@mozilla.org/observer-service;1"].
  1086.                getService(Components.interfaces.nsIObserverService);
  1087.             var topic = (notifyData == "more-chunk") ? "ybookmark.syncInfo" : "ybookmark.syncDone"; 
  1088.             os.notifyObservers(syncDoneSubject, topic, notifyData);
  1089.             }, 
  1090.             DEL_ADD_BOOKMARK_WAIT + 100, notifyData);
  1091.          },
  1092.  
  1093.          onerror: function(event) {
  1094.             yDebug.print("UNABLE to access user's delicious posts: " + event.target.status, YB_LOG_MESSAGE);
  1095.             yDebug.print("Notify the observer to update all windows\n");
  1096.                   
  1097.             YBookmarksSyncService._isSyncing = false;
  1098.             Components.classes["@mozilla.org/observer-service;1"].
  1099.               getService(Components.interfaces.nsIObserverService).
  1100.                  notifyObservers(null, "ybookmark.syncDone", "sync-error");
  1101.          }
  1102.       };
  1103.  
  1104.       var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  1105.                       .getService(Components.interfaces.nsIPrefBranch);
  1106.       var size = prefs.getIntPref("extensions.ybookmarks@yahoo.sync.chunk.size");
  1107.       if (size >= 0 && DEL_CHUNK_SIZE != size) {
  1108.          DEL_CHUNK_SIZE = size;
  1109.       }      
  1110.       delreader.allBookmarks(start, DEL_CHUNK_SIZE, cb);
  1111.    },
  1112.    
  1113.    QueryInterface: function(aIID) {
  1114.  
  1115.       if ( !aIID.equals(nsIYBookmarkSyncService) &&
  1116.            !aIID.equals(nsISupports)) {
  1117.          throw Components.results.NS_ERROR_NO_INTERFACE;
  1118.       }
  1119.  
  1120.       return this;
  1121.    }
  1122. };
  1123.  
  1124. /**
  1125.  * Microsummaries observer. This object is a listener for the microsummary object.
  1126.  * When microsummary content is available, it set the microsummary object to the
  1127.  * bookmark resource
  1128.  * 
  1129.  * @param bookmarksResource Bookmark in question
  1130.  */
  1131. function _YBookmarksSyncMicrosummaryUpdater(bookmarksResource, generatorUri) {
  1132.   this._bookmarksResource = bookmarksResource;
  1133.   this._generatorUri = generatorUri;
  1134.   this._msService = Components.classes["@mozilla.org/microsummary/service;1"].
  1135.                                 getService( Components.interfaces.nsIMicrosummaryService );
  1136. }
  1137.  
  1138. _YBookmarksSyncMicrosummaryUpdater.prototype = {
  1139.   interfaces: [ Components.interfaces.nsIMicorsummaryObserver, Components.interfaces.nsISupports ],
  1140.  
  1141.   onContentLoaded: function( microsummary ) {
  1142.     if ( microsummary.generator.uri.spec == this._generatorUri &&  microsummary.content ) {
  1143.       this._msService.setMicrosummary( this._bookmarksResource, microsummary );
  1144.     }
  1145.   },
  1146.  
  1147.   onElementAppended: function ( microsummary ) {
  1148.     if ( microsummary.generator.uri.spec == this._generatorUri ) {
  1149.       microsummary.update();
  1150.     }
  1151.   },
  1152.  
  1153.   updateMicrosummary: function(microsummaries) {
  1154.     var enumeration = microsummaries.Enumerate();
  1155.     while ( enumeration.hasMoreElements() ) {
  1156.       var microsummary = enumeration.getNext();
  1157.       microsummary.QueryInterface( Components.interfaces.nsIMicrosummary );
  1158.       if ( microsummary.generator.uri.spec == this._generatorUri ) {
  1159.         if ( microsummary.content ) {
  1160.           this._msService.setMicrosummary( this._bookmarksResource, microsummary );
  1161.         } else {
  1162.           microsummary.update();
  1163.         }
  1164.       }
  1165.     }
  1166.   } // end of updateMicrosummary
  1167. };
  1168.  
  1169. /**
  1170.  * Bookmarks Sync helper functions
  1171.  */
  1172. var _YBookmarksSyncHelper = {
  1173.   /**
  1174.    * This function does the following steps:
  1175.    *   1. Check if microsummary was saved at the service provider.
  1176.    *   2. If so, get the available microsummary uris for the url in question.
  1177.    *   3. Find the microsummary which was used by the user, add the observer
  1178.    *      to that microsummary in order to listen to it.
  1179.    */
  1180.   fetchMicrosummary: function( post ) {
  1181.     try {
  1182.  
  1183.       var generatorUri = post.getProperty("microsummary");
  1184.       if ( generatorUri == "" ) {
  1185.         yDebug.print ( "Generator uri is null", YB_LOG_MESSAGE );
  1186.         return;
  1187.       }
  1188.  
  1189.       // check if microsummary available
  1190.       var microsummaryService = Components.classes["@mozilla.org/microsummary/service;1"];
  1191.       if ( !microsummaryService ) {
  1192.         yDebug.print ( "Microsummary service not available", YB_LOG_MESSAGE );
  1193.         return;
  1194.       }
  1195.       microsummaryService = microsummaryService.getService( Components.interfaces.nsIMicrosummaryService );
  1196.  
  1197.       var url = post.getProperty("url");
  1198.         
  1199.       var bookmarksStore = (Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  1200.              getService(Components.interfaces.nsIYBookmarksStoreService));
  1201.  
  1202.       var bookmarksResource = bookmarksStore.isBookmarked ( url );
  1203.       if ( !bookmarksResource ) {
  1204.         yDebug.print ( "fetchMicrosummary: " + url + " is not in bookmark database\n", YB_LOG_MESSAGE );
  1205.         return;
  1206.       }
  1207.  
  1208.       // if bookmark already has microsummary, no need to continue
  1209.       var currentMicrosummary = microsummaryService.getMicrosummary( bookmarksResource );
  1210.       if ( currentMicrosummary ) {
  1211.         yDebug.print ( "fetchMicrosummary: " + url + " is already having the microsummary attached",
  1212.                      YB_LOG_MESSAGE);
  1213.         return;
  1214.       }
  1215.  
  1216.       var ioService= Components.classes["@mozilla.org/network/io-service;1"].
  1217.                    getService(Components.interfaces.nsIIOService);
  1218.  
  1219.       // fetch the microsummary uris for the "url". If not already in memory, microsummary service
  1220.       // load the page and collect the microsummaries from it.
  1221.       var microsummaries = microsummaryService.getMicrosummaries( ioService.newURI( url, null, null ),
  1222.                                                                   null
  1223.                                                                 );
  1224.       // microsummaries for this url does not exists, ignore the microsummary from the service provider
  1225.       if ( !microsummaries ) {
  1226.         yDebug.print ( "fetchMicrosummary: Either microsummaries is null or no elements within it for " + generatorUri + "(" + url + ")", YB_LOG_MESSAGE );
  1227.         return;
  1228.       }
  1229.  
  1230.       var observer = new _YBookmarksSyncMicrosummaryUpdater(bookmarksResource, generatorUri);
  1231.       microsummaries.addObserver( observer );
  1232.       observer.updateMicrosummary(microsummaries);
  1233.  
  1234.     } catch ( e ) {
  1235.       // microsummary property is not present. Bookmark do not have microsummary
  1236.       // attached to it.
  1237.       if ( e.stack )
  1238.         yDebug.print ( e.stack, YB_LOG_MESSAGE );
  1239.     }
  1240.   },
  1241.   
  1242.   /**
  1243.    * Update the bookmark store
  1244.    *
  1245.    * @param bookmarkStore a bookmark store service
  1246.    * @post post from the service provider
  1247.    */
  1248.   updateStoreFromPost: function(bookmarksStore, post) {
  1249.  
  1250.     var url = post.getProperty("url");
  1251.     var title = post.getProperty("title");
  1252.     var notes = post.getProperty("notes");
  1253.     var tags = post.getProperty("tags");
  1254.     var icon = post.getProperty("icon");
  1255.     var hash = post.getProperty("hash");
  1256.     var metahash = post.getProperty("metahash");
  1257.     var shortcut = post.getProperty("shortcut");
  1258.  
  1259.     var taglist = tags.split(/\s */);
  1260.  
  1261.     // public by default
  1262.     var shared = "true";
  1263.     try {
  1264.       shared = post.getProperty("shared");
  1265.       if ( !shared )
  1266.         shared = "true";
  1267.     } catch ( e ) {
  1268.     }
  1269.  
  1270.     /* this is from server so localOnly should be false */    
  1271.     var localOnly = "false";
  1272.     
  1273.     var postData = "";    
  1274.     if ( notes.length ) {
  1275.       postData = _YBookmarksSyncHelper.extractPostDataFromNotes( notes );
  1276.       if (postData.length)
  1277.         notes = _YBookmarksSyncHelper.removePostDataFromNotes( notes );
  1278.     }
  1279.  
  1280.     // This is a livemark. For all services, set the tags as firefox:rss to 
  1281.     // indicate the rss feed
  1282.     if ( ybookmarksUtils.containsTag( tags, "firefox:rss" ) != -1 ) {
  1283.       bookmarksStore.addLivemark( url, title, url, notes,
  1284.                                   taglist.length, taglist, shared, localOnly, false );
  1285.       bookmarksStore.setBookmarkKeyAsString(url, "hash", hash); 
  1286.       bookmarksStore.setBookmarkKeyAsString(url, "metahash", metahash); 
  1287.     } else {
  1288.  
  1289.       var bookmarkObject = {
  1290.         name: title,
  1291.         url: url,
  1292.         description: notes,
  1293.         tags: ybookmarksUtils.jsArrayToNs(taglist),
  1294.         shortcut: shortcut,
  1295.         postData: postData
  1296.       };
  1297.  
  1298.       try {
  1299.         var updateTime = post.getProperty("update_time");
  1300.         if ( updateTime ) {
  1301.           bookmarkObject.last_modified = updateTime;
  1302.         }
  1303.       } catch ( e ) {
  1304.       }
  1305.  
  1306.       try {
  1307.         var addTime = post.getProperty("add_time");
  1308.         if ( addTime ) {
  1309.           bookmarkObject.added_date = addTime;
  1310.         }
  1311.       } catch ( e ) {
  1312.       }
  1313.  
  1314.       bookmarkObject.shared = shared;
  1315.       bookmarkObject.localOnly = localOnly;
  1316.  
  1317.       bookmarksStore.addBookmarkObject( bookmarkObject, false );
  1318.       bookmarksStore.setBookmarkKeyAsString(url, "hash", hash); 
  1319.       bookmarksStore.setBookmarkKeyAsString(url, "metahash", metahash); 
  1320.       _YBookmarksSyncHelper.fetchMicrosummary( post );
  1321.     }
  1322.   },
  1323.   
  1324.   /**
  1325.    * Update the hashes for a bookmark. This is called after adding/editing a bookmark
  1326.    * to the service provider
  1327.    *
  1328.    * @param url a bookmark's url
  1329.    */
  1330.   updateBookmarkHash : function(url) {
  1331.     
  1332.     yDebug.print("UpdateBookmarkHash: " + url);
  1333.  
  1334.     var cb = { 
  1335.       onload: function(posts) {
  1336.       
  1337.         var bookmarksStore =
  1338.             (Components.classes["@mozilla.org/ybookmarks-store-service;1"].
  1339.              getService(Components.interfaces.nsIYBookmarksStoreService));
  1340.  
  1341.         var post;
  1342.         for (var i = 0; i < posts.length; i++) {
  1343.            post = posts.queryElementAt(i,
  1344.                                    Components.interfaces
  1345.                                    .nsIWritablePropertyBag);           
  1346.            _YBookmarksSyncHelper.updateStoreFromPost( bookmarksStore, post );
  1347.          }
  1348.         
  1349.          bookmarksStore.flush(false); 
  1350.       },
  1351.       
  1352.       onerror : function (posts) {
  1353.          yDebug.print("UNABLE to update bookmark based on the url", YB_LOG_MESSAGE);
  1354.       }
  1355.     };
  1356.   
  1357.     var delreader = Components.classes[kDelContractID].
  1358.                       getService(Components.interfaces.nsISocialStore);             
  1359.     delreader.getBookmarkForURL(url, cb);
  1360.   },
  1361.   
  1362.   /**
  1363.    * Extract the post data (POST form shortcut) from the notes string
  1364.    *  
  1365.    * @param notes the notes string 
  1366.    * @return postData the post data from the notes
  1367.    */
  1368.   extractPostDataFromNotes : function(notes) {
  1369.       
  1370.     var postData = "";
  1371.     if ( notes ) {
  1372.        var result = notes.match( /\[postdata:([^\]]+)\]\s*/ );
  1373.        try {
  1374.          if( result != null) {
  1375.            postData = decodeURIComponent(result[1]);
  1376.            yDebug.print( "Found postData: " + postData);
  1377.          }
  1378.        }
  1379.        catch(e) { }
  1380.     }
  1381.  
  1382.     
  1383.   return postData;
  1384.   },
  1385.   
  1386.   removePostDataFromNotes : function(notes) {
  1387.   
  1388.    if ( notes && notes.match( /\[postdata:/ ) ) {
  1389.      notes = notes.replace( /\[postdata:[^\]]+\]\s*/, "" );
  1390.    }
  1391.  
  1392.   return notes;
  1393.   }
  1394.   
  1395. };
  1396.  
  1397. /* 
  1398.  * Class factory
  1399. */
  1400. var YBookmarksSyncServiceFactory = {
  1401.  
  1402.    _singletonObj: null,
  1403.  
  1404.    createInstance: function(aOuter, aIID) {
  1405.  
  1406.       yDebug.print ( "createInstance called in nsIFactory object" );
  1407.  
  1408.       if ( aOuter != null ) {
  1409.          throw Components.results.NS_ERROR_NO_AGGREGATION;
  1410.       }
  1411.  
  1412.       if ( !this._singletonObj ) {
  1413.          YBookmarksSyncService.init();
  1414.          this._singletonObj = YBookmarksSyncService;
  1415.       }
  1416.       
  1417.       return this._singletonObj.QueryInterface(aIID);
  1418.    }
  1419. };
  1420.  
  1421. /*
  1422.  * Module definition
  1423. */
  1424.  
  1425. var YBookmarksSyncServiceModule = {
  1426.    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
  1427.       yDebug.print( "Registering YBookmarksSyncServiceModule", YB_LOG_MESSAGE);
  1428.       yDebug.print( "registerSelf: aFileSpec => " + aFileSpec );
  1429.       yDebug.print( "registerSelf: aLocation => " + aLocation );
  1430.       yDebug.print( "registerSelf: aType => " + aType );
  1431.       aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1432.       aCompMgr.registerFactoryLocation( CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
  1433.    },
  1434.  
  1435.    unregisterSelf: function (aCompMgr, aLocation, aType) {
  1436.       yDebug.print ( "unregisterSelf: aLocation => " + aLocation, YB_LOG_MESSAGE );
  1437.       yDebug.print ( "unregisterSelf: aType => " + aType );
  1438.       aCompMgr.QueryInterface( Components.interfaces.nsIComponentRegistrar);
  1439.       aCompMgr.unregisterFactoryLocation( CLASS_ID, aLocation );
  1440.    },
  1441.  
  1442.    getClassObject: function(aCompMgr, aCID, aIID) {
  1443.       yDebug.print ( "getClassObject: aCID => " + aCID );
  1444.       yDebug.print ( "getClassObject: aIID => " + aIID );
  1445.       if ( !aIID.equals(Components.interfaces.nsIFactory) ) 
  1446.          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1447.  
  1448.       if ( aCID.equals( CLASS_ID ) ) {
  1449.          return YBookmarksSyncServiceFactory;
  1450.       }
  1451.  
  1452.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1453.    },
  1454.  
  1455.    canUnload: function(aCompMgr) {
  1456.       return true;
  1457.    }
  1458. };
  1459.  
  1460. function NSGetModule(aCompMgr, aFileSpec) {
  1461.    yDebug.print( "YBookmarksSyncServiceModule GetModule" );
  1462.    return YBookmarksSyncServiceModule;
  1463. }
  1464.  
  1465.